"use strict";
/**
 * @ Author: Yoann Meclot. MSay2
 * @ Created on: 2020-08-28 11:42:33
 * @ Modified on: 2021-06-17 14:09:42
 *
 * @version 1.6
 */
/**
 * Copyright (c) 2020-2021 MSay2 (Yoann Meclot) - @msay2/tspoet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.TSType = void 0;
const Exceptions_js_1 = require("../util/Exceptions.js");
const ImportablesNamesComparator_js_1 = require("../util/ImportablesNamesComparator.js");
const ArrayList_js_1 = require("../util/list/ArrayList.js");
const Arrays_js_1 = require("../util/list/Arrays.js");
const HashSetPool_js_1 = require("../util/list/HashSetPool.js");
const Objects_js_1 = require("../util/Objects.js");
const Utils_js_1 = require("../util/Utils.js");
const CodeWriter_js_1 = require("./CodeWriter.js");
const Modifier_js_1 = require("./Modifier.js");
const TSPoet_js_1 = require("./TSPoet.js");
/**
 * TSType allows to you build a new element type.
 *
 * TSType can build Class, Interface and Enum.
 *
 * @class `TSType`
 * @version 1.6
 */
class TSType {
    constructor(builder) {
        this.mKind = builder.mKind;
        this.mObjectName = builder.mObjectName;
        this.mImports = builder.mImports;
        this.mDocs = builder.mDocs;
        this.mComments = builder.mComments;
        this.mAnnotations = builder.mAnnos;
        this.mGenericNames = builder.mGenericNames;
        this.mSuperclass = builder.mSuperClass;
        this.mImpls = builder.mImpls;
        this.mFields = builder.mFields;
        this.mMethods = builder.mMethods;
        this.mModifiers = builder.mModifiers;
        this.mNestedClass = builder.mNestedClass;
        this.mConstants = builder.mConstants;
        this.mProperties = builder.mProperties;
    }
    /**
     * Build Class.
     *
     * @param name The object class name.
     * @throws {TSPoetException} Check if the name is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    static classBuilder(objectName) {
        return new TSType.Builder(TSPoet_js_1.Kind.CLASS, objectName);
    }
    /**
     * Build anonymous implementation.
     *
     * @type {TSType.Builder}
     * @since 1.6
     */
    static anonymousImplBuilder() {
        return new TSType.Builder(TSPoet_js_1.Kind.UNDEFINED, null);
    }
    /**
     * Build enumeration.
     *
     * @param objectName The object enumeration name.
     * @type {TSType.Builder}
     * @since 1.0
     */
    static enumBuilder(objectName) {
        return new TSType.Builder(TSPoet_js_1.Kind.ENUM, objectName);
    }
    /**
     * Build interface.
     *
     * @param objectName The object interface name.
     * @type {TSType.Builder}
     * @since 1.0
     */
    static interfaceBuilder(objectName) {
        return new TSType.Builder(TSPoet_js_1.Kind.INTERFACE, objectName);
    }
    emitClass(codeWriter) {
        codeWriter.emitDocs(this.mDocs);
        if (!this.mAnnotations.isEmpty())
            for (let entry of this.mAnnotations.entries())
                codeWriter.emit(entry.getValue().emit(new CodeWriter_js_1.CodeWriter(codeWriter.getCount())).toString());
        codeWriter.emitComments(this.mComments);
        this.emitModifiers(codeWriter);
        this.mName = this.mObjectName.asType().getName();
        codeWriter.emitKindClass();
        codeWriter.emit(` ${this.mName}`);
        codeWriter.emitGenericNames(this.mGenericNames);
        codeWriter.emitSuperClass(this.mSuperclass);
        codeWriter.emitImplementations(this.mImpls);
        codeWriter.emitStartBlock();
        if (!this.mFields.isEmpty()) {
            for (let entry of this.mFields.entries()) {
                codeWriter.emit(entry.getValue().emit(new CodeWriter_js_1.CodeWriter(codeWriter.getCount())).toString());
                if (!entry.isLastData())
                    codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
                else {
                    if (!this.mMethods.isEmpty() || !this.mNestedClass.isEmpty())
                        codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
                }
            }
        }
        if (!this.mMethods.isEmpty()) {
            if (!this.mFields.isEmpty())
                codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
            for (let entry of this.mMethods.entries()) {
                codeWriter.emit(entry.getValue().emit(new CodeWriter_js_1.CodeWriter(codeWriter.getCount())).toString());
                if (!entry.isLastData())
                    codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE).emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
            }
        }
        if (!this.mNestedClass.isEmpty()) {
            if (!this.mMethods.isEmpty() || !this.mFields.isEmpty()) {
                codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
                if (!this.mMethods.isEmpty())
                    codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
            }
            codeWriter.emitNestedClass(this.mNestedClass);
        }
        codeWriter.emitEndBlock();
        if (!this.mNestedClass.isEmpty()) {
            let modifier = this.mModifiers.get(0);
            codeWriter.emitNameSpaceForNestedClass((modifier == null ? Modifier_js_1.Modifier.UNDEFINED : modifier), this.mObjectName, this.mNestedClass);
        }
        this.emitNeededImports(codeWriter);
        return codeWriter;
    }
    emitEnum(codeWriter) {
        codeWriter.emitDocs(this.mDocs);
        codeWriter.emitComments(this.mComments);
        this.emitModifiers(codeWriter);
        this.mName = this.mObjectName.asType().getName();
        codeWriter.emitKindEnum();
        codeWriter.emit(` ${this.mName}`);
        codeWriter.emitStartBlock();
        if (!this.mConstants.isEmpty()) {
            for (let entry of this.mConstants.entries()) {
                codeWriter.emit(entry.getValue().emit(new CodeWriter_js_1.CodeWriter(codeWriter.getCount())).toString());
                if (!entry.isLastData())
                    codeWriter.emit(CodeWriter_js_1.CodeWriter.COMMA).emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
            }
        }
        codeWriter.emitEndBlock();
        return codeWriter;
    }
    emitInterface(codeWriter) {
        codeWriter.emitDocs(this.mDocs);
        codeWriter.emitComments(this.mComments);
        this.emitModifiers(codeWriter);
        this.mName = this.mObjectName.asType().getName();
        codeWriter.emitKindInterface();
        codeWriter.emit(` ${this.mName}`);
        codeWriter.emitGenericNames(this.mGenericNames);
        codeWriter.emitSuperClass(this.mSuperclass);
        codeWriter.emitStartBlock();
        if (!this.mProperties.isEmpty()) {
            for (let entry of this.mProperties.entries()) {
                codeWriter.emit(entry.getValue().emit(new CodeWriter_js_1.CodeWriter(codeWriter.getCount())).toString());
                if (!entry.isLastData())
                    codeWriter.emit(CodeWriter_js_1.CodeWriter.NEW_LINE);
                if (entry.isLastData())
                    codeWriter.emit(" ");
            }
        }
        codeWriter.emitEndBlock();
        this.emitNeededImports(codeWriter);
        return codeWriter;
    }
    ensureOnlyOneType(target) {
        let list = new ArrayList_js_1.ArrayList();
        let set = new HashSetPool_js_1.HashSetPool();
        for (let entry of target.entries()) {
            let objectImport = entry.getValue();
            if (!Objects_js_1.Objects.isNull(objectImport) && !Utils_js_1.UString.isEmpty(objectImport.asType().getName()) && !Utils_js_1.UString.isEmpty(objectImport.asImport().getPath())) {
                if (set.put(objectImport))
                    list.add(objectImport);
            }
        }
        list.sort(ImportablesNamesComparator_js_1.ImportablesNamesComparator.ascending());
        set.clear();
        set = undefined;
        return list;
    }
    emitModifiers(codeWriter) {
        if (!this.mModifiers.isEmpty())
            if (!this.mModifiers.contains(Modifier_js_1.Modifier.PUBLIC) || !this.mModifiers.contains(Modifier_js_1.Modifier.STATIC))
                codeWriter.emitModifiers(this.mModifiers);
    }
    emitNeededImports(codeWriter) {
        if (this.mImports != null && !this.mImports.isEmpty())
            codeWriter.emitImports(this.mName, this.ensureOnlyOneType(this.mImports));
    }
}
exports.TSType = TSType;
TSType.Builder = class IBuilder {
    constructor(kind, name) {
        this.mImports = new ArrayList_js_1.ArrayList();
        this.mDocs = new ArrayList_js_1.ArrayList();
        this.mComments = new ArrayList_js_1.ArrayList();
        this.mAnnos = new ArrayList_js_1.ArrayList();
        this.mImpls = new ArrayList_js_1.ArrayList();
        this.mFields = new ArrayList_js_1.ArrayList();
        this.mMethods = new ArrayList_js_1.ArrayList();
        this.mNestedClass = new ArrayList_js_1.ArrayList();
        this.mConstants = new ArrayList_js_1.ArrayList();
        this.mProperties = new ArrayList_js_1.ArrayList();
        this.mModifiers = new ArrayList_js_1.ArrayList();
        this.mGenericNames = new ArrayList_js_1.ArrayList();
        this.mKind = kind;
        this.mObjectName = name;
    }
    /**
     * Add a new documentation.
     *
     * @param arg Your documentation.
     * @throws {TSPoetException} Check if the arg is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addDoc(arg) {
        this.mDocs.add(Utils_js_1.Preconditions.checkNotNull(arg, "documentation", "string"));
        return this;
    }
    /**
     * Add a new annotation.
     *
     * @param annotation The annotation to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.interfaceBuilder static method.
     * @throws {TSPoetException} Check if the TSAnnotation object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addAnnotation(annotation) {
        this.checkAnonymous("annotation");
        this.checkEnum("annotation");
        this.checkInterface("annotation");
        this.mAnnos.add(Utils_js_1.Preconditions.checkNotNull(annotation, "annotation", "TSAnnotation object"));
        for (let entry of annotation.mImports.entries())
            this.putImport(entry.getValue());
        return this;
    }
    /**
     * Add modifiers.
     *
     * @param modifiers The modifiers to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the modifiers is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addModifiers(...modifiers) {
        this.checkAnonymous("modifiers");
        this.mModifiers = Arrays_js_1.Arrays.asList(Utils_js_1.Preconditions.checkNotNull(modifiers, "modifiers", "modifiers"));
        return this;
    }
    /**
     * Add constant.
     *
     * @param tsConstant The constant to be added.
     * @throws {TSPoetException} Check if the build is not initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.interfaceBuilder static method.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addConstant(tsConstant) {
        if (this.mKind != TSPoet_js_1.Kind.ENUM)
            throw new Exceptions_js_1.TSPoetException("Only enumeration can contain constants.");
        this.checkInterface("constant");
        this.mConstants.add(Utils_js_1.Preconditions.checkNotNull(tsConstant, "constant", "TSConstant object"));
        return this;
    }
    /**
     * Add comment.
     *
     * @param arg The comment to be added.
     * @throws {TSPoetException} Check if the arg is undefined or null.
     * @type {TSType.Builder}
     * @since 1.6
     */
    addComment(arg) {
        this.mComments.add(Utils_js_1.Preconditions.checkNotNull(arg, "comment", "string"));
        return this;
    }
    /**
     * Add property.
     *
     * @param tsProperty The property to be added.
     * @throws {TSPoetException} Check if the build is not initialized by TSType.interfaceBuilder static method.
     * @throws {TSPoetException} Check if TSProperty object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addProperty(tsProperty) {
        if (this.mKind != TSPoet_js_1.Kind.INTERFACE)
            throw new Exceptions_js_1.TSPoetException("Only interface can contain properties.");
        this.mProperties.add(Utils_js_1.Preconditions.checkNotNull(tsProperty, "properties", "TSProperty object"));
        return this;
    }
    /**
     * Add generics names.
     *
     * @param names The generics names to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if names is undefined or null.
     * @type {TSType.Builder}
     * @since 1.6
     */
    addGenericNames(...names) {
        this.checkAnonymous("generic names");
        this.checkEnum("generic names");
        this.mGenericNames = Arrays_js_1.Arrays.asList(Utils_js_1.Preconditions.checkNotNull(names, "generic names", "names"));
        return this;
    }
    /**
     * Set super class.
     *
     * @param superClass The super class type.
     * @throws {TSPoetException} Check if the build initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if the super class object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    setSuperclass(superClass) {
        this.checkAnonymous("super class");
        this.checkEnum("super class");
        this.mSuperClass = Utils_js_1.Preconditions.checkNotNull(superClass, "super class", " super class");
        this.putImport(superClass);
        return this;
    }
    /**
     * Add interface implementation.
     *
     * @param impl The implementation to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.interfaceBuilder static method.
     * @throws {TSPoetException} Check if the impl object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addImplementation(impl) {
        this.checkAnonymous("implementation");
        this.checkEnum("implementation");
        this.checkInterface("implementation");
        this.mImpls.add(Utils_js_1.Preconditions.checkNotNull(impl, "implementation", "ObjectName object"));
        this.putImport(impl);
        return this;
    }
    /**
     * Add field.
     *
     * @param field The field to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.interfaceBuilder static method.
     * @throws {TSPoetException} Check if the TSField object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addField(field) {
        this.checkEnum("field");
        this.checkInterface("field");
        this.mFields.add(Utils_js_1.Preconditions.checkNotNull(field, "field", "TSField object"));
        for (let entry of field.mImports.entries()) {
            let object = entry.getValue();
            this.putImport(object);
            for (let entry of object.getCasts().entries())
                this.putImport(entry.getValue());
        }
        return this;
    }
    /**
     * Add method.
     *
     * @param method The method to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.interfaceBuilder static method.
     * @throws {TSPoetException} Check if the TSMethod object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.0
     */
    addMethod(method) {
        this.checkEnum("method");
        this.checkInterface("method");
        this.mMethods.add(Utils_js_1.Preconditions.checkNotNull(method, "method", "TSMethod object"));
        for (let entry of method.mImports.entries()) {
            let object = entry.getValue();
            this.putImport(object);
            for (let entry of object.getCasts().entries())
                this.putImport(entry.getValue());
        }
        return this;
    }
    /**
     * Add nested class.
     *
     * @param clazz The class to be added.
     * @throws {TSPoetException} Check if the build initialized by TSType.anonymousBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.enumBuilder static method.
     * @throws {TSPoetException} Check if the build initialized by TSType.interfaceBuilder static method.
     * @throws {TSPoetException} Check if the class object is undefined or null.
     * @type {TSType.Builder}
     * @since 1.6
     */
    addNestedClass(clazz) {
        this.checkAnonymous("nested class");
        this.checkEnum("nested class");
        this.checkInterface("nested class");
        this.mNestedClass.add(Utils_js_1.Preconditions.checkNotNull(clazz, "nested class", "TSType object"));
        for (let entry of clazz.mImports.entries())
            this.putImport(entry.getValue());
        return this;
    }
    /**
     * Build object type.
     *
     * @type {TSType}
     * @since 1.0
     */
    build() {
        return new TSType(this);
    }
    putImport(objectName) {
        this.mImports.add(objectName);
    }
    checkAnonymous(message) {
        if (this.mKind == TSPoet_js_1.Kind.UNDEFINED)
            throw new Exceptions_js_1.TSPoetException(`Unable to add ${message} in anonymous implementation.`);
    }
    checkEnum(message) {
        if (this.mKind == TSPoet_js_1.Kind.ENUM)
            throw new Exceptions_js_1.TSPoetException(`Unable to add ${message} in enumeration.`);
    }
    checkInterface(message) {
        if (this.mKind == TSPoet_js_1.Kind.INTERFACE)
            throw new Exceptions_js_1.TSPoetException(`Unable to add ${message} in interface.`);
    }
};
//# sourceMappingURL=TSType.js.map